package org.web30.nntp;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringBufferInputStream;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.james.mime4j.field.ContentTypeField;
import org.apache.james.mime4j.field.FieldName;
import org.apache.james.mime4j.field.Fields;
import org.apache.james.mime4j.message.BinaryBody;
import org.apache.james.mime4j.message.Body;
import org.apache.james.mime4j.message.BodyFactory;
import org.apache.james.mime4j.message.BodyPart;
import org.apache.james.mime4j.message.Header;
import org.apache.james.mime4j.message.Message;
import org.apache.james.mime4j.message.Multipart;
import org.apache.james.mime4j.message.TextBody;


public class Util {

    public static final String DEFAULT_ENCODING = "ISO-8859-1";
    public static final String UTF8_ENCODING = "UTF-8";

    public static void pipe(Reader in,
            OutputStream out)
            throws UnsupportedEncodingException,
            IOException {
        char[] buf = new char[1024];
        Writer writer = new OutputStreamWriter(out, DEFAULT_ENCODING);
        int read = 0;
        while ((read = in.read(buf)) > 0) {
            writer.write(buf, 0, read);
        }
        writer.flush();
    }

    public static Message getMimeObj(Reader in) {
        ByteArrayOutputStream out = null;
        try {
            out = new ByteArrayOutputStream();
            Util.pipe(in, out);
            ByteArrayInputStream msg = new ByteArrayInputStream(out.toByteArray());
            out.close();
            out = null;
            Message mime = new Message(msg);
            return mime;
        } catch (UnsupportedEncodingException ex) {
            return null;
        } catch (IOException ex) {
            return null;
        }
    }

    public static Message unifyMessageCharset(Message msg, String charset) {
        if (msg == null) {
            return null;
        }
        if (msg.isMultipart()) {
            Multipart newBody = unifyMultiPartCharset((Multipart) msg.getBody(), charset);
            if (newBody != null) {
                msg.removeBody();
                int count = newBody.getCount();
                //TODO: so far we just clear non-text bodypart, i.e. only text body can be left
                if (count != 1) {
                    msg = null;
                } else {
                    Header header = msg.getHeader();
                    header.removeFields(FieldName.CONTENT_TRANSFER_ENCODING);
                    BodyPart body = newBody.getBodyParts().get(0);
                    Header bodyHeader = body.getHeader();
                    if (bodyHeader.getField(FieldName.CONTENT_TYPE) != null) {
                        header.removeFields(FieldName.CONTENT_TYPE);
                        header.setField(bodyHeader.getField(FieldName.CONTENT_TYPE));
                    }
                    if (bodyHeader.getField(FieldName.CONTENT_TRANSFER_ENCODING) != null) {
                        header.removeFields(FieldName.CONTENT_TRANSFER_ENCODING);
                        header.setField(bodyHeader.getField(FieldName.CONTENT_TRANSFER_ENCODING));
                    }
                }
                msg.setBody(newBody);
            } else {
                msg = null;
            }
        } else {
            Header header = changeHeaderCharset(msg.getHeader(), charset);
            msg.setHeader(header);
            Body newBody = unifySingleBodyCharset(msg.getBody(), charset);
            if (newBody != null) {
                msg.removeBody();
                msg.setBody(newBody);
            } else {
                msg = null;
            }
        }
        return msg;
    }
    
    private static Header changeHeaderCharset(Header header, String charset) {
        ContentTypeField ctField = (ContentTypeField) header.getField(FieldName.CONTENT_TYPE);
        String mimeType = null;
        HashMap<String, String> newParas = new HashMap<String, String>();
        ContentTypeField newField ;
        if (ctField != null) {
            mimeType = ctField.getMimeType();
            Map<String, String> paras = ctField.getParameters();
            Iterator ite = paras.entrySet().iterator();
            while (ite.hasNext()) {
                Entry<String, String> entry = (Entry<String, String>) ite.next();
                if (entry.getKey().compareToIgnoreCase(ContentTypeField.PARAM_CHARSET) == 0) {
                    continue;
                } else {
                    newParas.put(entry.getKey(), entry.getValue());
                }
            }
        }
        if(mimeType == null) {
            mimeType = "text/plain";
        }
        newParas.put(ContentTypeField.PARAM_CHARSET, charset);
        newField = Fields.contentType(mimeType, newParas);
        header.removeFields(FieldName.CONTENT_TYPE);
        header.addField(newField);
        return header;
    }

    static Multipart unifyMultiPartCharset(Multipart parts, String charset) {
        List<BodyPart> bodyParts = parts.getBodyParts();
        List<BodyPart> newParts = new ArrayList<BodyPart>(bodyParts.size());
        for (BodyPart part : bodyParts) {
            BodyPart temp = unifyBodyPartCharset(part, charset);
            if (temp != null) {
                newParts.add(temp);
            }
        }
        if (newParts.size() != 0) {
            for (int i = 0; i < bodyParts.size(); i++) {
                parts.removeBodyPart(i);
            }
            parts.setBodyParts(newParts);
        } else {
            parts = null;
        }
        return parts;
    }

    private static BodyPart unifyBodyPartCharset(BodyPart bodyPart, String charset) {
        if (bodyPart == null) {
            return null;
        }
        if (!bodyPart.isMultipart()) {
            Body ret = unifySingleBodyCharset(bodyPart.getBody(), charset);
            if (ret != null) {
                bodyPart.removeBody();
                bodyPart.setBody(ret);
            } else {
                bodyPart = null;
            }
        } else {
            Multipart parts = unifyMultiPartCharset((Multipart) bodyPart.getBody(), charset);
            if (parts != null) {
                bodyPart.removeBody();
                bodyPart.setMultipart(parts);
            } else {
                bodyPart = null;
            }
        }
        return bodyPart;
    }

    private static Body unifySingleBodyCharset(Body body, String charset) {
        if (body instanceof TextBody) {
            TextBody temp = (TextBody) body;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            try {
                temp.writeTo(out);
            } catch (IOException ex) {
                return null;
            }
            BodyFactory factory = new BodyFactory();
            TextBody newBody = null;
            try {
                newBody = factory.textBody(new StringBufferInputStream(out.toString(charset)));
            } catch (IOException ex) {
                return null;
            }
            return newBody;
        } else if (body instanceof BinaryBody) {
            return null;
        }
        return body;
    }
}
